use std::fs;
use std::path::{Path, PathBuf};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use futures_util::StreamExt;

use axum::extract::multipart::Field;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum FileUtilsError {
    #[error("IO错误: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("文件操作错误: {0}")]
    FileOperation(String),
}

/// 创建目录（如果不存在）
pub fn create_dir_if_not_exists(path: &str) -> Result<(), FileUtilsError> {
    let path = Path::new(path);
    if !path.exists() {
        fs::create_dir_all(path)?;
    }
    Ok(())
}

/// 保存上传的文件
pub async fn save_uploaded_file<'a>(mut field: Field<'a>, path: &'a  &str) -> Result<(), FileUtilsError> {
    let path = Path::new(path);
    
    // 确保目录存在
    if let Some(parent) = path.parent() {
        tokio::fs::create_dir_all(parent).await?;
    }
    
    let mut file = File::create(path).await?;
    
    while let Some(chunk) = field.next().await {
        let data = chunk.map_err(|e| FileUtilsError::FileOperation(format!("读取文件块失败: {}", e)))?;
        file.write_all(&data).await?;
    }
    
    Ok(())
}

/// 获取文件大小
pub async fn get_file_size(path: &str) -> Result<u64, FileUtilsError> {
    let metadata = tokio::fs::metadata(path).await?;
    Ok(metadata.len())
}

/// 删除文件
pub async fn delete_file(path: &str) -> Result<(), FileUtilsError> {
    if tokio::fs::metadata(path).await.is_ok() {
        tokio::fs::remove_file(path).await?;
    }
    Ok(())
}

/// 列出目录中的文件
pub async fn list_files(dir: &str) -> Result<Vec<PathBuf>, FileUtilsError> {
    let mut entries = tokio::fs::read_dir(dir).await?;
    let mut files = Vec::new();
    
    while let Some(entry) = entries.next_entry().await? {
        let metadata = entry.metadata().await?;
        if metadata.is_file() {
            files.push(entry.path());
        }
    }
    
    Ok(files)
}

/// 格式化文件大小为人类可读的字符串
pub fn format_file_size(bytes: u64) -> String {
    if bytes < 1024 {
        return format!("{} B", bytes);
    } else if bytes < 1024 * 1024 {
        return format!("{:.1} KB", bytes as f64 / 1024.0);
    } else if bytes < 1024 * 1024 * 1024 {
        return format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0));
    } else {
        return format!("{:.1} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0));
    }
}
